package com.simba.server.jsonservice;

import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;

import com.simba.server.jsonservice.serivces.*;
import org.apache.log4j.Logger;
import org.jboss.netty.channel.Channel;
import org.json.JSONObject;
import org.json.JSONTokener;
import com.simba.common.RequestHandler;
import com.simba.common.jsonserver.JsonChannelManager;
import com.simba.common.jsonservice.DefaultJsonServiceContainer;
import com.simba.common.jsonservice.JsonService;
import com.simba.common.jsonservice.JsonServiceContainer;
import com.simba.common.log.LogUtil;
import com.simba.common.thread.CommonExecutorFactory;
import com.simba.common.thread.CounterThreadFactory;

public class JsonServiceManager implements RequestHandler
{
	private static class JsonServiceManagerHolder
	{
		private static final JsonServiceManager INSTANCE = new JsonServiceManager();
	}

	public static final String JSON_SERVICE_NAME = "ServiceName";
	protected final static Logger logger = Logger.getLogger(JsonServiceManager.class);
	private static final String ASYNC_OPERATION_EXECUTOR_NAME = "Json-Service-Task";
	private static int EXECUTOR_POOL_SIZE = 3;

	public static JsonServiceManager getInstance()
	{
		return JsonServiceManagerHolder.INSTANCE;
	}

	private Executor asyncJsonServiceExecutor;
	private BlockingQueue<Runnable> asyncJsonServiceOperationQueue;
	private final JsonServiceContainer container = new DefaultJsonServiceContainer();

	private JsonServiceManager()
	{

	}

	@Override
	public void handleRequest(final Channel channel, final Object requestObj)
	{
		final byte[] request = (byte[]) requestObj;

		Runnable serviceTask = new Runnable()
		{
			@Override
			public void run()
			{
				JsonService accessor = null;
				try
				{
					JSONObject requestJSONObject = JsonServiceManager.this
							.parseServiceJsonData(request);
					if (requestJSONObject == null)
					{
						return;
					}

					String serviceName = requestJSONObject
							.getString(JsonServiceManager.JSON_SERVICE_NAME);

					if (serviceName == null)
					{
						return;
					}

					accessor = JsonServiceManager.this.container.getJsonService(serviceName);
					if (accessor == null)
					{
						accessor = JsonServiceManager.this.container
								.getJsonService(ErrorJsonService.NAME);
					}

					if (accessor != null)
					{
						accessor.handleRequest(channel, requestJSONObject);
					}
				}
				catch (Exception ex)
				{
					LogUtil.LogError(JsonServiceManager.logger,
							"When Json server handlerRequest exception  ["
									+ channel.getRemoteAddress() + "] -- " + ex.getMessage());
					if (ex instanceof InterruptedException)
					{
						Thread.currentThread().interrupt();
					}
				}
			}
		};

		this.executeInThreadPool(serviceTask);
	}

	public void init()
	{
		this.initJsonServiceContainer();
		this.initAsyncJsonTaskExcutor();
	}

	public void postInitProcess()
	{
		LogUtil.LogDebug(JsonServiceManager.logger, "JsonServiceManager initialize successfully");
	}

	public void sendDataToRemoteLogger(Map<String, Object> map)
	{
		Channel[] clientChannels = JsonChannelManager.getInstance().getChannelList();
		JSONObject sortInfoObj = new JSONObject(map);
		for (Channel channel : clientChannels)
		{
			JsonChannelManager.getInstance().commitResponse(channel,
					sortInfoObj.toString().getBytes(), false);
		}
	}

	public void sendEchoDataToRemoteLogger()
	{
		Channel[] clientChannels = JsonChannelManager.getInstance().getChannelList();

		for (Channel channel : clientChannels)
		{
			String sortInfoContent = "{\"" + JsonServiceManager.JSON_SERVICE_NAME
					+ "\":\"Echo\",\"Channel-Id\":\"" + channel.getId() + "\"}";
			JsonChannelManager.getInstance().commitResponse(channel, sortInfoContent.getBytes(),
					false);
		}
	}

	private void executeInThreadPool(final Runnable executeRunnable)
	{
		/** *********** identify init command begin *********** */

		this.runAsyncJsonTask(executeRunnable);
	}

	private void initAsyncJsonTaskExcutor()
	{
		this.asyncJsonServiceOperationQueue = new LinkedBlockingQueue<Runnable>();
		this.asyncJsonServiceExecutor = CommonExecutorFactory.newFixedThreadPool(
				JsonServiceManager.EXECUTOR_POOL_SIZE,
				new CounterThreadFactory(JsonServiceManager.ASYNC_OPERATION_EXECUTOR_NAME),
				this.asyncJsonServiceOperationQueue);
	}

	private void initJsonServiceContainer()
	{
		this.container.addJsonService(ErrorJsonService.NAME, new ErrorJsonService());
		this.container.addJsonService(RenewTrashSlotJsonService.NAME, new RenewTrashSlotJsonService());
		this.container.addJsonService(SlotsUnlockJsonService.NAME, new SlotsUnlockJsonService());
		this.container.addJsonService(SlotsPackJsonService.NAME, new SlotsPackJsonService());
		this.container.addJsonService(RenewAllBoxInfos.NAME, new RenewAllBoxInfos());
		this.container.addJsonService(SortJsonService.NAME, new SortJsonService());
		this.container.addJsonService(LockSlotJsonService.NAME, new LockSlotJsonService());
		this.container.addJsonService(UnLockSlotJsonService.NAME, new UnLockSlotJsonService());
		this.container.addJsonService(ChangeModeJsonService.NAME, new ChangeModeJsonService());
		this.container.addJsonService(SlotPackJsonService.NAME, new SlotPackJsonService());
		this.container.addJsonService(ChangeUploadJsonService.NAME, new ChangeUploadJsonService());
		this.container.addJsonService(ScreenSlotInitJsonService.NAME, new ScreenSlotInitJsonService());
		this.container.addJsonService(ScreenBigInitJsonService.NAME, new ScreenBigInitJsonService());

	}

	private JSONObject parseServiceJsonData(byte[] request)
	{
		JSONObject requestJSONObject = null;
		try
		{
			String jsonString = new String(request, "UTF-8");
			JSONTokener jsonTokener = new JSONTokener(jsonString);
			requestJSONObject = (JSONObject) jsonTokener.nextValue();
		}
		catch (Exception ex)
		{
			LogUtil.LogError(JsonServiceManager.logger,
					"Parse Json service name exception -- " + ex.getMessage());
			requestJSONObject = null;
		}

		return requestJSONObject;
	}

	private void runAsyncJsonTask(Runnable task)
	{
		try
		{
			if (task != null)
			{
				this.asyncJsonServiceExecutor.execute(task);
			}
		}
		catch (Exception e)
		{
			LogUtil.LogError(JsonServiceManager.logger,
					"run async task to access json service request failed : " + e.getMessage());
		}
	}
}
